﻿/****************************************************************
*   作者：Schme
*   CLR版本：4.0.30319.42000
*   创建时间：2017/4/19 15:59:57
*   2017
*   描述说明：
*
*   修改历史：
*
*
*****************************************************************/
using Abp.Auditing;
using Abp.Domain.Repositories;
using Abp.Extensions;
using Abp.Net.Mail;
using Abp.Runtime.Caching;
using Abp.UI;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Mt.Site.Application.Members.Dto.FrontEnd;
using Mt.Site.Core.Members;
using Mt.Site.Core.Members.Features;
using Mt.Site.Core.Members.Values;
using Mt.Site.Core.Net.Emailing;
using Mt.Site.Core.Security;
using Mt.Site.Core.Url;
using Mt.Site.Core.VerifyCodes;
using Mt.Site.Core.Web;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace Mt.Site.Application.Members
{
    public class MemberFrontEndAppService:MtSiteFrontendAppServiceBase,IMemberFrontEndAppService
    {
        private readonly IMembersCache _membersCache;
        private readonly ICacheManager _cacheManager;
        private readonly IRepository<Member, Guid> _memberRepository;
        private readonly IMemberManager _memberManager;
        private readonly IEmailTemplateProvider _emailTemplateProvider;
        private readonly IEmailSender _email;
        private readonly ITenancyNameCache _tenancyNameCache;
        private readonly IWebUrlService _webUrlService;
        private readonly IVerifyCodeManager _verifyCodeManager;
        private readonly IClientInfoProvider _clientInfoProvider;
        private readonly IRSACryptoService _rSACryptoService;
        private readonly IHttpContextAccessor _httpContextAccessor;

        public MemberFrontEndAppService(
            IMembersCache membersCache,
            ICacheManager cacheManager,
            IRepository<Member, Guid> memberRepository,
            IMemberManager memberManager,
            IEmailTemplateProvider emailTemplateProvider,
            IEmailSender email, 
            IVerifyCodeManager verifyCodeManager,
            ITenancyNameCache tenancyNameCache,
            IWebUrlService webUrlService,
            IClientInfoProvider clientInfoProvider,
            IRSACryptoService rSACryptoService,
            IHttpContextAccessor httpContextAccessor)
        {
            _membersCache = membersCache;
            _cacheManager = cacheManager;
            _memberRepository = memberRepository;
            _memberManager = memberManager;
            _verifyCodeManager = verifyCodeManager;
            _emailTemplateProvider = emailTemplateProvider;
            _email = email;
            _tenancyNameCache = tenancyNameCache;
            _webUrlService = webUrlService;
            _clientInfoProvider = clientInfoProvider;
            _rSACryptoService = rSACryptoService;
            _httpContextAccessor = httpContextAccessor;
        }

        /// <summary>
        /// 会员注册之邮箱认证（发送）
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<string> Verification(VerificationDto input)
        {
            if (!IsEnabled(MemberFeatures.MemberRegisterFeature))
            {
                return ResultType.InvalidParameter.RawValue().ToString();
            }

            try
            {
                var reeamil = await _memberManager.GetEamil2(input.Email);
                if (reeamil == null)
                {
                    var htmlBody = _emailTemplateProvider.GetVerificationTemplate();

                    string key = await _verifyCodeManager.GetVerifyCode(input.Email, VerifyCodeType.RegisterMember);
                    //
                    htmlBody = htmlBody.Replace("%mail%", input.Email);
                    htmlBody = htmlBody.Replace("%VerificationDear%",L("VerificationDear"));
                    htmlBody = htmlBody.Replace("%VerificationContent%", L("VerificationContent")).Replace("%str%", key);
                    htmlBody = htmlBody.Replace("%VerificationCopyRights%", L("VerificationCopyRights"));
                    
                    string biaoti = L("VerificationTitle");

                    await _email.SendGroupAsync(biaoti, htmlBody, input.Email);
                    return ResultType.Success.RawValue().ToString();
                }
                else
                {
                    if (reeamil.IsActive == true)
                    {
                        return ResultType.InvalidParameter.RawValue().ToString();
                    }
                    else
                    {
                        return ResultType.Frozen.RawValue().ToString();
                    }
                }
            }
            catch (InvalidCastException e)
            {
                Logger.Debug(e.ToString());
                return e.ToString();
            }
        }
        
        /// <summary>
        /// 忘记密码（后期修改2017年6月30日）
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<ForgetDtoOut> Forget(ForgetDto input)
        {
            ForgetDtoOut re = new ForgetDtoOut();
            EmailinfoDto reeamil = null;

            if (IsEnabled(MemberFeatures.MemberResetPasswordFeature))
            {
                reeamil = await _memberManager.GetEamil2(input.userName);
            }

            if (reeamil !=null)
            {
                re.Email = reeamil.Email;
                if (reeamil.IsActive == true)
                {
                    var htmlBody = _emailTemplateProvider.GetForgetPasswordTemplate();

                    string key = await _verifyCodeManager.GetVerifyCode(re.Email, VerifyCodeType.ResetPassword);

                    string url = _webUrlService.GetSiteRootAddress(_tenancyNameCache.TenancyName, _tenancyNameCache.CultureName, true);
                    //string link = url + "Login/Reset/" + input.kind+"?key=" + key + "&email=" + re.Email + "";
                    string link = url + "Login/Reset/" + input.kind + "?key=" + key + "&email=" + re.Email + "";

                    htmlBody = htmlBody.Replace("%VerificationDear%", L("VerificationDear"));
                    htmlBody = htmlBody.Replace("%ForgetPasswordContent%", L("ForgetPasswordContent")).Replace("%url%", link);
                    htmlBody = htmlBody.Replace("%user%", input.userName);



                    string biaoti = L("ForgetPasswordTitle");
                    await _email.SendGroupAsync(biaoti, htmlBody, re.Email);
                    re.Status = ResultType.Success;
                }
                else
                {
                    re.Status = ResultType.Frozen;
                }
            }
            else
            {
                re.Status = ResultType.InvalidParameter;
            }
            return re;
        }

        /// <summary>
        /// 登陆
        /// </summary>
        /// <param name="input"></param>
        /// <returns>0:用户或密码不正确;1:成功;2:登陆失败(账号问题：冻结或无效)</returns>
        public async Task<ResultType> Login(LoginDto input)
        {
            if (!input.password.IsNullOrWhiteSpace())
            {
                input.password = _rSACryptoService.Decrypt(input.password);
            }

            var result = await _memberManager.LoginInfo(input.userName, input.password, input.remember);
            if (result.ResultType == ResultType.Success)
            {
                await _membersCache.Login(result.MemberCacheItem);
            }
            return result.ResultType;
        }

        /// <summary>
        /// 退出登陆
        /// </summary>
        public void Logout()
        {
            var item = _membersCache.GetMemberCookieItem();
            _membersCache.Logout(item?.UserId);
        }

        /// <summary>
        /// 注册用户
        /// </summary>
        /// <param name="input">0:用户名已经存在;1：注册成功;2:注册失败;3:邮箱已存在;4:验证码错误</param>
        public async Task<ResultType> RegisterMember(RegisterMemberDto input)
        {
            if (!IsEnabled(MemberFeatures.MemberRegisterFeature))
            {
                return ResultType.InvalidParameter;
            }

            // 判断验证码 
            if (!(await _verifyCodeManager.CheckVerifyCode(input.Email, VerifyCodeType.RegisterMember, input.valiCode)))
            {
                return ResultType.WrongVerifyCode;//验证码错误
            }



            if (await CheckIsExistEmailOrUserName(input.LoginName))
            {
                return ResultType.InvalidParameter;                   // 账号已存在
            }
            if (await CheckIsExistEmailOrUserName(input.Email))
            {
                return ResultType.EmailHasUsed;                       // 邮箱已存在
            }

            string ip = _clientInfoProvider.ClientIpAddress;
            if (ip.IndexOf(',') > 0)
            {
                ip = (ip.Split(',')[0]).Split(':')[0];
            }

            var member = new Member
            {
                LoginName = input.LoginName,
                Password = input.Password,
                Email = input.Email,
                IsActive = true,
                RegisterForm = input.RegisterForm,
                IP = ip,
                IsReceive = input.IsReceive
            };
            var memberInfo = ObjectMapper.Map<MemberInfo>(input);
            memberInfo.MemberID = await _memberManager.RegisterMember(member);
            await _memberManager.AddOrUpdateMemberInfoAsync(memberInfo);

            return ResultType.Success;
        }
       
        /// <summary>
        /// 校验邮箱或用户名是否存在
        /// </summary>
        /// <param name="emailOrUserName">邮箱或用户名</param>
        /// <returns>true:存在；false:不存在</returns>
        public async Task<bool> CheckIsExistEmailOrUserName(string emailOrUserName)
        {
            return await _memberRepository.CountAsync(t=>t.Email==emailOrUserName||t.LoginName==emailOrUserName)>0;
        }

        /// <summary>
        /// 获取Email
        /// </summary>
        /// <param name="EmailOrUsername"></param>
        /// <returns></returns>
        public async Task<string> GetEamil(string EmailOrUsername)
        {
            var query = from m in _memberRepository.GetAll()
                        where (m.LoginName == EmailOrUsername || m.Email == EmailOrUsername) && m.IsActive == true
                        select m.Email;
            var result = await query.FirstOrDefaultAsync();
            return result;
        }

        /// <summary>
        /// 重置密码
        /// </summary>
        /// <param name="input"></param>
        /// <returns>0:邮箱不存在,1：重置成功；2：重置失败;3:两次新密码输入不一致;4:验证码错误</returns>
        public async Task<ResultType> ResetPassword(ResetPasswordDto input)
        {
            if (!IsEnabled(MemberFeatures.MemberResetPasswordFeature))
            {
                return ResultType.InvalidParameter;
            }

            // 判断验证码 
            if (!(await _verifyCodeManager.CheckVerifyCode(input.Email, VerifyCodeType.ResetPassword, input.Keynum)))
            {
                return ResultType.WrongVerifyCode;//验证码错误
            }

            if (input.NewPassword != input.ReNewPassword)
            {
                return ResultType.Fail;
            }

            var member = await _memberRepository.FirstOrDefaultAsync(t => t.Email == input.Email && t.IsActive == true);
            if (member != null)
            {
                member.Password = input.NewPassword;
                CheckVerificationCode(null, member);

				_membersCache.Logout(member.Id);

                return ResultType.Success;
            }
            else
            {
                return ResultType.Fail;
            }
        }

        /// <summary>
        /// 修改密码
        /// </summary>
        /// <param name="input"></param>
        /// <returns>0:账号或密码不正确；1：重置成功；2：重置失败;3:两次新密码输入不一致;4:验证码错误</returns>
        public async Task<ResultType> ChangePassword(ChangePasswordDto input)
        {
            //获取验证码
            string sessionId = _httpContextAccessor.HttpContext.Request.Cookies[MtSiteConsts.CookieNames.SessionCookieName];
            if (sessionId.IsNullOrEmpty())
            {
                throw new UserFriendlyException("用户浏览器不支持Cookie");//本地化
            }
            string code = _cacheManager.GetVerificationCodeCache().Get(sessionId, () => null);
            _cacheManager.GetVerificationCodeCache().Remove(sessionId);

            if (code == null || code.ToLower() != input.ValiCode.ToLower())
            {
                return ResultType.WrongVerifyCode;
            }

            if (input.NewPassword != input.ReNewPassword)
            {
                return ResultType.Fail;
            }

            var memberCacheItem = await GetMemberCacheItem();
            if (memberCacheItem == null)
            {
                return ResultType.Fail;
            }
            var member = await _memberRepository.FirstOrDefaultAsync(t => t.Email == memberCacheItem.Email && t.Password == input.CurrentPassword && t.IsActive == true);
            if (member != null)
            {
                member.Password = input.NewPassword;
                CheckVerificationCode(memberCacheItem, member);

				if (input.ShouldLoginOut)
				{
					_membersCache.Logout(member.Id);
				}
				else
				{
					await _membersCache.Update(memberCacheItem);
				}

				return ResultType.Success;
            }
            else
            {
                return ResultType.Fail;
            }
        }
        
        /// <summary>
        /// 修改Email
        /// </summary>
        /// <param name="input"></param>
        /// <returns>0:未获取到登陆信息；1：修改成功;2:邮箱已存在</returns>
        public async Task<ResultType> ChangeEmail(ChangeEmailDto input)
        {
            if (!IsEnabled(MemberFeatures.MemberChangeEmailFeature))
            {
                return ResultType.InvalidParameter;
            }

            var memberCacheItem = await GetMemberCacheItem();
            if (memberCacheItem != null)
            {
                if (await CheckIsExistEmailOrUserName(input.NewEmail))
                {
                    return ResultType.Fail;
                }
                var member = await _memberRepository.GetAsync(memberCacheItem.MemberID);
                if (member.Password != input.Password.ToMd5().ToLower())
                {
                    return ResultType.InvalidParameter;
                }
                member.Email = input.NewEmail;
                memberCacheItem.Email = input.NewEmail;
                CheckVerificationCode(memberCacheItem, member);
                await _memberRepository.UpdateAsync(member);

				if (input.ShouldLoginOut)
				{
					_membersCache.Logout(member.Id);
				}
				else
				{
					await _membersCache.Update(memberCacheItem);
				}
				return ResultType.Success;
            }
            return ResultType.InvalidParameter;
        }

        /// <summary>
        /// 修改用户名
        /// </summary>
        /// <param name="input"></param>
        /// <returns>0:未获取到登陆信息；1：修改成功;2:用户名已存在</returns>
        public async Task<ResultType> ChangeUserName(ChangeUserNameDto input)
        {
            if (!IsEnabled(MemberFeatures.MemberChangeUserNameFeature))
            {
                return ResultType.InvalidParameter;
            }

            var memberCacheItem = await GetMemberCacheItem();
            if (memberCacheItem != null)
            {
                if (await CheckIsExistEmailOrUserName(input.NewUserName))
                {
                    return ResultType.Fail;
                }
                var member = await _memberRepository.GetAsync(memberCacheItem.MemberID);

                if (member.Password != input.Password.ToMd5().ToLower())
                {
                    return ResultType.InvalidParameter;
                }

                member.LoginName = input.NewUserName;
                memberCacheItem.LoginName = input.NewUserName;
                CheckVerificationCode(memberCacheItem, member);
                await _memberRepository.UpdateAsync(member);
				if (input.ShouldLoginOut)
				{
					_membersCache.Logout(member.Id);
				}
				else
				{
					await _membersCache.Update(memberCacheItem);
				}

				return ResultType.Success;
            }
            return ResultType.InvalidParameter;
        }

        //重置安全戳，从而将其他设备登陆上的本用户踢下线
        private void CheckVerificationCode(MemberCacheItem memberCacheItem, Member member)
        {
            if (IsEnabled(MemberFeatures.MemberSecurityStampFeature))
            {
                var code = Guid.NewGuid().ToString();
                member.VerificationCode = code;
                if (memberCacheItem != null)
                {
                    memberCacheItem.VerificationCode = code;
                }
            }
        }


        /// <summary>
        /// 修改用户信息
        /// </summary>
        /// <param name="input"></param>
        /// <returns>0:未获取到登陆信息；1：修改成功;</returns>
        public async Task<ResultType> UpdateMemberInfo(UpdateMemberInfoFrontEndDto input)
        {
            if (!IsEnabled(MemberFeatures.MemberUpdateMemberInfoFeature))
            {
                return ResultType.InvalidParameter;
            }

            var memberCacheItem = await GetMemberCacheItem();
            if (memberCacheItem != null)
            {
                var isMemberInfoLocalizated = IsEnabled(MemberFeatures.MemberInfoLocalization);
                var memberInfo = await _memberManager.FirstOrDefaultMemberInfoAsync(memberCacheItem.MemberID, isMemberInfoLocalizated);
                memberInfo = ObjectMapper.Map<MemberInfo>(input);

                await _memberManager.AddOrUpdateMemberInfoAsync(memberInfo);

                return ResultType.Success;
            }

            // 未登陆
            return ResultType.InvalidParameter;
        }

        /// <summary>
        /// 获取用户信息
        /// </summary>
        /// <returns></returns>
        public async Task<MemberInfoDto> GetMemberInfo(MemberCacheItem memberCacheItem)
        {
            if (memberCacheItem != null)
            {
                var member = await _memberRepository.FirstOrDefaultAsync(c => c.Id == memberCacheItem.MemberID && c.IsActive);
                if(member != null)
                {
                    var isMemberInfoLocalizated = IsEnabled(MemberFeatures.MemberInfoLocalization);
                    var memberInfo = await _memberManager.FirstOrDefaultMemberInfoAsync(memberCacheItem.MemberID, isMemberInfoLocalizated);

                    if (memberInfo != null)
                    {
                        var dto = ObjectMapper.Map<MemberInfoDto>(memberInfo);
                        dto.LoginName = member.LoginName;
                        dto.Email = member.Email;
                        dto.IsTrainingAllowed = member.IsTrainingAllowed;
                        return dto;
                    }
                }
            }

            return null;
        }

        public async Task<MemberCacheItem> GetMemberCacheItem()
        {
            var memberCookieItem = _membersCache.GetMemberCookieItem();

            if (memberCookieItem == null)  //cookie有问题或未登陆
            {
                _membersCache.Logout(null);
                //登陆过期
                return null;
            }

            var memberCacheItem = await _membersCache.TryGetMemberInfo(memberCookieItem.UserId);
            if (memberCacheItem == null)//缓存过期
            {
                memberCacheItem = await _memberManager.GetMemberCacheItemById(memberCookieItem);
                if (memberCacheItem != null) //cookie值正常
                {
                    if (memberCacheItem.VerificationCode != memberCookieItem.Code)
                    {
                        _membersCache.Logout(memberCookieItem.UserId);
                        return null;
                    }
                    //缓存
                    await _membersCache.Update(memberCacheItem);
                }
            }
            else
            {
                if (memberCacheItem.VerificationCode != memberCookieItem.Code)
                {
                    _membersCache.Logout(memberCookieItem.UserId);
                    return null;
                }
            }

            
            if (memberCacheItem == null)
            {
                _membersCache.Logout(memberCookieItem.UserId);
            }
            return memberCacheItem;
        }
    }
}
